#include "stdafx.h"

#include "CppUnitTest.h"

#include <HSModel/HeightMap.h>
#include <HSModel/HeightMapCoordinates.h>
#include <HSModel/GenerateSurface.h>
#include <HSModel/GroundTypes.h>
#include <HSModel/CreateHill.h>
#include <HSModel/SharedConstants.h>

using namespace Microsoft::VisualStudio::CppUnitTestFramework;

namespace HSModelTests
  {		
  TEST_CLASS(UnitTestForSurface)
    {
    public:

      TEST_METHOD(SurfaceTestFor3rdTriangle)
        {
        HeightMap map(5,5);
        map.Set(0,0,5);
        map.Set(0,1,3);
        map.Set(0,2,7);
        map.Set(0,3,3);
        map.Set(0,4,7);

        map.Set(1,0,2);
        map.Set(1,1,1);
        map.Set(1,2,0);
        map.Set(1,3,3);
        map.Set(1,4,7);

        map.Set(2,0,5);
        map.Set(2,1,4);
        map.Set(2,2,3);
        map.Set(2,3,3);
        map.Set(2,4,7);

        map.Set(3,0,5);
        map.Set(3,1,4);
        map.Set(3,2,3);
        map.Set(3,3,3);
        map.Set(3,4,7);

        map.Set(4,0,4);
        map.Set(4,1,4);
        map.Set(4,2,5);
        map.Set(4,3,3);
        map.Set(4,4,7);

        double origin_x = 0;
        double origin_y = 0;
        double origin_z = 0;
        Model3D model;

        GenerateSurface(model, map, origin_x, origin_y, origin_z, 1); 

        auto it = model.Triangles().begin();
        for(int i = 0; i < 3; i++) // Get to the 4th element
          it++; 

        Assert::AreEqual(0.0, it->GetPoint(0).Z());
        Assert::AreEqual(1.0, it->GetPoint(1).Z());
        Assert::AreEqual(7.0, it->GetPoint(2).Z());
        }
      TEST_METHOD(SurfaceTestFor2ndTriangle)
        {
        HeightMap map(5,5);
        map.Set(0,0,5);
        map.Set(0,1,3);
        map.Set(0,2,7);
        map.Set(0,3,3);
        map.Set(0,4,7);

        map.Set(1,0,2);
        map.Set(1,1,1);
        map.Set(1,2,0);
        map.Set(1,3,3);
        map.Set(1,4,7);

        map.Set(2,0,5);
        map.Set(2,1,4);
        map.Set(2,2,3);
        map.Set(2,3,3);
        map.Set(2,4,7);

        map.Set(3,0,5);
        map.Set(3,1,4);
        map.Set(3,2,3);
        map.Set(3,3,3);
        map.Set(3,4,7);

        map.Set(4,0,4);
        map.Set(4,1,4);
        map.Set(4,2,5);
        map.Set(4,3,3);
        map.Set(4,4,7);

        double origin_x = 0;
        double origin_y = 0;
        double origin_z = 0;
        Model3D model;

        GenerateSurface(model, map, origin_x, origin_y, origin_z, 1); 

        auto it = model.Triangles().begin();
        for(int i = 0; i < 2; i++) // Get to the 3th element
          it++; 

        Assert::AreEqual(1.0, it->GetPoint(0).Z());
        Assert::AreEqual(3.0, it->GetPoint(1).Z());
        Assert::AreEqual(7.0, it->GetPoint(2).Z());
        }
      TEST_METHOD(SurfaceTestTrianglesOnEdge)
        {
        HeightMap map(5,5);
        map.Set(0,0,5);
        map.Set(0,1,3);
        map.Set(0,2,7);
        map.Set(0,3,3);
        map.Set(0,4,7);

        map.Set(1,0,2);
        map.Set(1,1,1);
        map.Set(1,2,0);
        map.Set(1,3,3);
        map.Set(1,4,7);

        map.Set(2,0,5);
        map.Set(2,1,4);
        map.Set(2,2,3);
        map.Set(2,3,3);
        map.Set(2,4,7);

        map.Set(3,0,5);
        map.Set(3,1,4);
        map.Set(3,2,3);
        map.Set(3,3,3);
        map.Set(3,4,7);

        map.Set(4,0,4);
        map.Set(4,1,4);
        map.Set(4,2,5);
        map.Set(4,3,3);
        map.Set(4,4,7);

        double origin_x = 0;
        double origin_y = 0;
        double origin_z = 0;

        Model3D model;

        GenerateSurface(model, map, origin_x, origin_y, origin_z, 1); 

        auto it = model.Triangles().begin();
        for(int i = 0; i < 30; i++) // Get to the 31st element
          it++; 
       
        Assert::AreEqual(3.0, it->GetPoint(0).Z());
        Assert::AreEqual(3.0, it->GetPoint(1).Z());
        Assert::AreEqual(7.0, it->GetPoint(2).Z());

        it++; //Get to the 32nd element
        Assert::AreEqual(7.0, it->GetPoint(0).Z());
        Assert::AreEqual(3.0, it->GetPoint(1).Z());
        Assert::AreEqual(7.0, it->GetPoint(2).Z());
        }
      TEST_METHOD(SurfaceTestTriangleArrayLength)
        {
        HeightMap map(3,3);
        map.Set(0,0,5);
        map.Set(0,1,3);
        map.Set(0,2,7);

        map.Set(1,0,2);
        map.Set(1,1,1);
        map.Set(1,2,0);

        map.Set(2,0,5);
        map.Set(2,1,4);
        map.Set(2,2,3);

        double origin_x = 0;
        double origin_y = 0;
        double origin_z = 0;

        Model3D model;

        GenerateSurface(model, map, origin_x, origin_y, origin_z, 1); 

        int size = 0;
        for(auto it = model.Triangles().begin(); it != model.Triangles().end(); it++)
          size++;

        Assert::AreEqual(8, size);
        }
      TEST_METHOD(GroundTypesApplyingTest)
        {
        HeightMap map(5,5);
        map.Set(0,0,5);
        map.Set(0,1,3);
        map.Set(0,2,7);
        map.Set(0,3,3);
        map.Set(0,4,7);

        map.Set(1,0,2);
        map.Set(1,1,1);
        map.Set(1,2,0);
        map.Set(1,3,3);
        map.Set(1,4,7);

        map.Set(2,0,5);
        map.Set(2,1,4);
        map.Set(2,2,3);
        map.Set(2,3,3);
        map.Set(2,4,7);

        map.Set(3,0,5);
        map.Set(3,1,4);
        map.Set(3,2,3);
        map.Set(3,3,3);
        map.Set(3,4,7);

        map.Set(4,0,4);
        map.Set(4,1,4);
        map.Set(4,2,5);
        map.Set(4,3,3);
        map.Set(4,4,7);

        double origin_x = 0;
        double origin_y = 0;
        double origin_z = 0;

        Model3D model;

        TGroundTypeMap ground_types;
        std::vector<EGroundType> tmp_vect;
        for(size_t i = 0; i < map.GetSizeX() - 1; ++i)
          {
          tmp_vect.reserve(map.GetSizeY());
          for(size_t j = 0; j < map.GetSizeY() - 1; ++j)
            {
            tmp_vect.push_back(GT_GRASS);
            }
          ground_types.push_back(tmp_vect);
          tmp_vect.clear();
          }
        ground_types[0][0] = GT_WALKWAY;
        ground_types[0][1] = GT_WALKWAY;
        ground_types[0][2] = GT_WALKWAY;

        GenerateSurface(model, map, origin_x, origin_y, origin_z, 1);
        ApplyGroundTypes(model, map, 0, 0, 0, 1, ground_types);

        auto iterator = model.Triangles().begin();
        
        Assert::AreEqual(0.5, iterator->GetPointUV(0).X());
        Assert::AreEqual(1.0, iterator->GetPointUV(0).Y());

        Assert::AreEqual(0.5, iterator->GetPointUV(1).X());
        Assert::AreEqual(0.0, iterator->GetPointUV(1).Y());

        Assert::AreEqual(1.0, iterator->GetPointUV(2).X());
        Assert::AreEqual(0.0, iterator->GetPointUV(2).Y());

        }

      TEST_METHOD(GeneratePartOfSurfaceTest)
        {
        //Arrange
        HeightMap map(5,5);
        map.Set(0,0,1);
        map.Set(0,1,1);
        map.Set(0,2,1);
        map.Set(0,3,1);
        map.Set(0,4,1);
                    
        map.Set(1,0,1);
        map.Set(1,1,1);
        map.Set(1,2,1);
        map.Set(1,3,1);
        map.Set(1,4,1);
                    
        map.Set(2,0,1);
        map.Set(2,1,1);
        map.Set(2,2,1);
        map.Set(2,3,1);
        map.Set(2,4,1);
                    
        map.Set(3,0,1);
        map.Set(3,1,1);
        map.Set(3,2,1);
        map.Set(3,3,1);
        map.Set(3,4,1);
                    
        map.Set(4,0,1);
        map.Set(4,1,1);
        map.Set(4,2,1);
        map.Set(4,3,1);
        map.Set(4,4,1);

        size_t base_x = 2, base_y = 2;

        std::vector<TVector3> changed;
        std::vector<std::vector<bool>> visited;
        visited.reserve(map.GetSizeX());
        for(size_t i = 0; i < map.GetSizeX(); ++i)
          {
          std::vector<bool> tmp;
          tmp.reserve(map.GetSizeY());
          for(size_t j = 0; j < map.GetSizeY(); ++j)
            {	
            tmp.push_back(false);
            }
          visited.push_back(tmp);
          }

        Model3D model;

        changed.push_back(HeightMapUtils::ConvertHMapPointToTVector(1, 1, 1));
        changed.push_back(HeightMapUtils::ConvertHMapPointToTVector(1, 2, 1));
        changed.push_back(HeightMapUtils::ConvertHMapPointToTVector(1, 3, 1));
        changed.push_back(HeightMapUtils::ConvertHMapPointToTVector(2, 1, 1));
        changed.push_back(HeightMapUtils::ConvertHMapPointToTVector(2, 2, 1));
        changed.push_back(HeightMapUtils::ConvertHMapPointToTVector(2, 3, 1));
        changed.push_back(HeightMapUtils::ConvertHMapPointToTVector(3, 1, 1));
        changed.push_back(HeightMapUtils::ConvertHMapPointToTVector(3, 2, 1));
        changed.push_back(HeightMapUtils::ConvertHMapPointToTVector(3, 3, 1));

        //Act
        GeneratePartOfSurface(model, changed, GroundConstants::ORIGIN_X, GroundConstants::ORIGIN_Y, GroundConstants::ORIGIN_Z, GroundConstants::CELL_SIZE);

        size_t triangle_count = 0;
        for(auto it = model.Triangles().begin(); it != model.Triangles().end(); ++it)
          {
          triangle_count++;
          }

        //Assert
        size_t real = 8;
        Assert::AreEqual(real, triangle_count);
        }

      TEST_METHOD(PartOfSurfaceShouldNotContainUnnecessaryPointsTest)
        {
        std::vector<TVector3> changed_points;
        changed_points.push_back(TVector3(10, 0, 11));
        changed_points.push_back(TVector3(0, 0, 11));
        changed_points.push_back(TVector3(0, 10, 11));
        Model3D model;
        GeneratePartOfSurface(model, changed_points, GroundConstants::ORIGIN_X, GroundConstants::ORIGIN_Y, GroundConstants::ORIGIN_Z, GroundConstants::CELL_SIZE);
        size_t point_counter = 0;
        for(auto iter = model.Points().begin(); iter != model.Points().end(); ++iter)
          ++point_counter;
        Assert::AreEqual(size_t(4), point_counter);
        }
    };
  }
